<?php
/**
 * Created by PhpStorm.
 * User: juwangkeji
 * Date: 2020-03-24
 * Time: 09:35
 */

namespace controllers\admin;
use controllers\AdminApi;
use models\SysModule;
use system\Addon;
use system\MyError;
use system\Tool\DataBase;

/**
 * Class index
 * @package controllers\admin
 * @auth    模块管理
 */
class Modules extends AdminApi {
    /**
     * 首页方法
     */
    public function index() {
        // 读取插件
        $dir = glob(APP_DIR . 'addons/*/bootstrap.php');
        $addons = [];
        $modulesModel = new SysModule();
        $state = ['未安装', '已安装'];
        foreach ($dir as $key => $val) {
            require_once $val;
            preg_match('/addons\/([\w]+)/', $val, $match);
            list(, $name) = $match;
            $className = 'addons\\' . $name . '\\' . ucfirst($name);
            if (class_exists($className)) {
                $addon = new $className;
                $methodClass = new \ReflectionClass($addon);
                $methods = $methodClass->getMethods(\ReflectionMethod::IS_PUBLIC);
                $install = 0;
                $uninstall = 0;
                foreach ($methods as $v) {
                    if ($v->getName() == 'install' || $v->getName() == 'uninstall') {
                        if ($v->class == $className) {
                            $methodName = $v->getName();
                            $$methodName = 1;
                        }
                    }
                }
                if ($addon instanceof Addon) {
                	$version = $addon->getVersion();
                	$version = is_array($version) ? array_pop($version) : $version;
                    $item = [
                        'id' => $key + 1,
                        'author' => $addon->getAuthor(),
                        'name' => $addon->getName(),
                        'desc' => $addon->getDesc(),
                        'version' => $version,
                        'install' => $install,
						'have_update' => 0 , // 是否有更新
						'upgrade' => '' , // 升级信息
                        'uninstall' => $uninstall,
                        'state' => 0,  // 0 未安装 1 已安装
                    ];
                    // 如果没有提供install和卸载方法，则默认无需安装
                    if (!$item['install'] && !$item['uninstall']) {
                        $item['state'] = 1;
                    } else {
                        $find = $modulesModel->where(['name' => $item['name']])->find();
                        if ($find) {
                            $item['state'] = 1;
                            $upgrade = $methodClass->hasMethod('upgrade') ? $addon->upgrade() : [$version => ''];
                            if ($find['version'] < str_replace('.' , '' , $version)){
                            	$item['have_update'] = 1;
                            	$item['upgrade'] = $upgrade[$version];
							}
                        }
                    }
                    $item['stateFormat'] = $state[$item['state']];
                    $addons[] = $item;
                }
            }
        }
        return self::returnJson($addons);
    }

    /**
     * 获取对象
     * @param $name
     * @return mixed|string
     */
    public function getClass($name) {
        $file = str_replace('*', $name, APP_DIR . 'addons/*/bootstrap.php');
        if (!is_file($file)) {
            return false;
        }
        require_once $file;
        $className = 'addons\\' . $name . '\\' . ucfirst($name);
        $class = class_exists($className);
        if (!$class) {
            return false;
        }
        return new $className;
    }

    /**
     * 获取方法是否存在
     * @param $name
     * @param $class
     * @param $method
     * @return bool
     * @throws \ReflectionException
     */
    public function getMethodExists($name, $class, $method) {
        $className = 'addons\\' . $name . '\\' . ucfirst($name);
        $methodClass = new \ReflectionClass($class);
        $methods = $methodClass->getMethods(\ReflectionMethod::IS_PUBLIC);
        foreach ($methods as $v) {
            if ($v->getName() == 'install' || $v->getName() == 'uninstall') {
                if ($v->class == $className && $v->getName() == $method) {
                    return true;
                }
            }
        }
        return false;
    }

	/**
	 * 模块升级
	 * @return string
	 * @throws \ReflectionException
	 * @author Colin <amcolin@126.com>
	 * @date 2021-12-28 下午10:42
	 */
    public function upgrade(){
    	$name = self::$post['name'];
    	$version = self::$post['version'];
    	if (!$name){
    		return error('无效的插件名');
		}
		$class = $this->getClass($name);
		if (!$class) {
			return error('模块不存在');
		}
		$state = $this->getMethodExists($name, $class, 'install');
		if (!$state) {
			return error('不存在安装脚本，安装失败');
		}
		$model = new SysModule();
		if (!$find = $model->getFind(['name' => $name])) {
			return error('该模块未安装');
		}
		$versionNumber = $this->getNumberVersion($version);
		if ($versionNumber < $find['version']){
			return error('安装的版本不对。请检查最新的版本号');
		}
		try {
			$sql = $class->install();
			if(!isset($sql[$version])){
				throw new MyError('没有需要安装的sql语句');
			}
			$sql = $sql[$version];
			$status = $this->execute($sql);
			if ($status) {
				$model->update(['id' => $find['id'] , 'version' => $versionNumber]);
				return success('升级成功');
			}
			return success('升级失败');
		} catch (MyError $e){
			return error('升级失败：' . $e->getMessage());
		} catch (\Exception $e) {
			return error('升级失败：' . $e->getMessage());
		}
	}

    /**
     * 安装脚本
     * @param string $name
     * @return string
	 * @throws
     */
    public function install($name = '') {
        $class = $this->getClass($name);
        if (!$class) {
            return error('模块不存在');
        }
        $version = $this->getNumberVersion($class->getVersion());
        $state = $this->getMethodExists($name, $class, 'install');
        if (!$state) {
            return error('不存在安装脚本，安装失败');
        }
		$model = new SysModule();
        if ($model->getFind(['name' => $name])) {
            return error('该模块已安装');
        }
        $dependents = $class->dependents();
        if ($dependents){
        	// 检查
			foreach ($dependents as $dependent){
				if(!addonIs($dependent)){
					return error($dependent . '模块没有安装');
				}
			}
		}
        try {
            $sql = $class->install();
            $status = $this->execute($sql);
            if ($status) {
				$model->create(['name' => $name, 'create_time' => time(), 'uid' => $this->uid , 'version' => $version]);
                $model->insert();
                return success('安装成功');
            }
            return success('安装失败');
        } catch (MyError $e){
			return error('安装失败：' . $e->getMessage());
		} catch (\Exception $e) {
            return error('安装失败：' . $e->getMessage());
        }
    }

    /**
     * 卸载
     * @param string $name
     * @return string
	 * @throws
     */
    public function uninstall($name = '') {
        $class = $this->getClass($name);
        if (!$class) {
            return error('模块不存在');
        }
        $state = $this->getMethodExists($name, $class, 'uninstall');
        if (!$state) {
            return error('不存在卸载脚本，卸载失败');
        }
        $model = new SysModule();
        if (!$model->getFind(['name' => $name])) {
            return error('该模块已卸载');
        }
        try {
            $sql = $class->uninstall();
            $status = $this->execute($sql);
            if ($status) {
                $model->where(['name' => $name])->delete();
                return success('卸载成功');
            }
            return success('卸载失败');
        } catch (\Exception $e) {
            return error('卸载失败：' . $e->getMessage());
        }
    }

	/**
	 * 执行
	 * @param $sql
	 * @return bool
	 * @throws MyError
	 * @author Colin <amcolin@126.com>
	 * @date 2021-12-28 下午9:46
	 */
    protected function execute($sql) {
        if (is_bool($sql) && $sql === true) {
            $status = true;
        } else {
            if (is_string($sql)) {
                $sql = [$sql];
            }
			try {
				$status = $this->execSql($sql);
			}catch (MyError $e){
            	throw new MyError($e->getMessage());
			}
        }
        return $status;
    }

    /**
     * 运行SQL
     * @param array $sql
     * @return bool
     * @throws MyError
     */
    protected function execSql($sql = []) {
        $tool = new DataBase();
        $db = $tool->getDB();
        $db->startTransaction();
        // 把所有的语句都执行一遍
        foreach ($sql as $key => $value) {
        	// 支持数组
        	if (is_array($value)){
        		// 去除多余的;号
        		$value = implode(';' , array_map(function($a){
					return rtrim($a , ';');
				} , $value));
			}
            $value && $value = $this->parseTableName($value);
			try {
				if ($value && $tool->execute($value) < 1) {
					$db->rollback();
					throw new MyError('SQL语句执行错误 [' . $value . ']');
				}
			}catch (MyError $e){
				throw new MyError($e->getMessage());
			}
        }
        $db->commit();
        return true;
    }

    /**
     * 解析表名
     *
     * @param string $sql
     *
     * @return mixed
     */
    protected function parseTableName($sql = '') {
        $prefix = Config('DB_PREFIX');
        return str_replace('@', $prefix ?: '', $sql);
    }

    /**
     * 载入模板
     * @param $name
     * @return false|string
     * @throws MyError
     */
    public function template($name){
        list($name , $method) = explode('/' , $name);
        list($control , $action) = explode('-' , $method);
        if (!$action){
            $action = 'index';
        }
        if ($name){
            $class = $this->getClass($name);
            if (!$class){
                return error('无效的模块');
            }
            $path = '%saddons/%s/template/%s-%s.html';
            $file = sprintf($path  ,APP_DIR , $name , $control , $action);
            if (!is_file($file)){
                return error('模板不存在');
            }
            return file_get_contents($file);
        }
    }

	/**
	 * 获取数字的版本号
	 * @param $version
	 * @return string|string[]
	 * @author Colin <amcolin@126.com>
	 * @date 2021-12-28 下午5:20
	 */
    protected function getNumberVersion($version){
		$version = is_array($version) ? $version : [$version];
    	return str_replace('.' , '' , array_pop($version));
	}
}